home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / Source / Chapter 12 / Game / PlayerObject.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-10-01  |  18.8 KB  |  546 lines

  1. //-----------------------------------------------------------------------------
  2. // PlayerObject.h implementation.
  3. // Refer to the PlayerObject.h interface for more details.
  4. //
  5. // Programming a Multiplayer First Person Shooter in DirectX
  6. // Copyright (c) 2004 Vaughan Young
  7. //-----------------------------------------------------------------------------
  8. #include "Main.h"
  9.  
  10. //-----------------------------------------------------------------------------
  11. // The player object class constructor.
  12. //-----------------------------------------------------------------------------
  13. PlayerObject::PlayerObject( PlayerInfo *player, Script *script, unsigned long type ) : AnimatedObject( script->GetStringData( "mesh_name" ), script->GetStringData( "mesh_path" ), type )
  14. {
  15.     // Set the player's DirectPlay ID.
  16.     m_dpnid = player->dpnid;
  17.  
  18.     // Set the players's name.
  19.     m_name = new char[strlen( player->name ) + 1];
  20.     strcpy( m_name, player->name );
  21.  
  22.     // Players start with full health;
  23.     m_health = 100.0f;
  24.     m_dying = false;
  25.  
  26.     // Indicate that the view transform is not being taken from this player.
  27.     m_isViewing = false;
  28.  
  29.     // Clear the player's score.
  30.     m_frags = 0;
  31.     m_deaths = 0;
  32.  
  33.     // Player objects start off invisible and disabled.
  34.     SetVisible( false );
  35.     SetEnabled( false );
  36.  
  37.     // Clear the player's input.
  38.     m_drive = 0.0f;
  39.     m_strafe = 0.0f;
  40.     m_fire = false;
  41.  
  42.     // Set the correct ellipse radius.
  43.     SetEllipsoidRadius( *script->GetVectorData( "ellipse_radius" ) );
  44.  
  45.     // Level the player's view tilt.
  46.     m_viewTilt = 0.0f;
  47.  
  48.     // Set the default view smoothing and sensitivity.
  49.     m_viewSmoothing = 0.5f;
  50.     m_viewSensitivity = 0.5f;
  51.  
  52.     // Create the callback data used for tracking the player's foot steps.
  53.     m_callbackData[0].foot = 0;
  54.     m_callbackData[1].foot = 1;
  55.  
  56.     // Create the callback keys. The second key time is set per animation.
  57.     D3DXKEY_CALLBACK keys[2];
  58.     keys[0].Time = 0;
  59.     keys[0].pCallbackData = &m_callbackData[0];
  60.     keys[1].pCallbackData = &m_callbackData[1];
  61.  
  62.     LPD3DXKEYFRAMEDANIMATIONSET oldAS;
  63.     LPD3DXCOMPRESSEDANIMATIONSET newAS;
  64.     LPD3DXBUFFER buffer;
  65.  
  66.     // Go through the four movement animations and set the foot step keys.
  67.     for( char a = 1; a < 5; a++ )
  68.     {
  69.         // Get the old animation.
  70.         GetAnimationController()->GetAnimationSet( a, (LPD3DXANIMATIONSET*)&oldAS );
  71.  
  72.         // Set the time for the second key.
  73.         keys[1].Time = float( oldAS->GetPeriod() / 2.0f * oldAS->GetSourceTicksPerSecond() );
  74.  
  75.         // Compress the old animation set.
  76.         oldAS->Compress( D3DXCOMPRESS_DEFAULT, 0.4f, NULL, &buffer );
  77.  
  78.         // Create the new animation using the old one and the foot step keys.
  79.         D3DXCreateCompressedAnimationSet( oldAS->GetName(), oldAS->GetSourceTicksPerSecond(), oldAS->GetPlaybackType(), buffer, 2, keys, &newAS );
  80.         SAFE_RELEASE( buffer );
  81.  
  82.         // Unregister the old animation set.
  83.         GetAnimationController()->UnregisterAnimationSet( oldAS );
  84.         SAFE_RELEASE( oldAS );
  85.  
  86.         // Register the new animation set.
  87.         // Note: The new animation is appended to the end of the list.
  88.         GetAnimationController()->RegisterAnimationSet( newAS );
  89.         SAFE_RELEASE( newAS );
  90.     }
  91.  
  92.     // Play the idle animation.
  93.     PlayAnimation( 0, 0.0f );
  94.  
  95.     // Create the step sound audio paths.
  96.     m_leftStepAudioPath = new AudioPath3D;
  97.     m_rightStepAudioPath = new AudioPath3D;
  98.  
  99.     // Set the friction on this object.
  100.     SetFriction( 8.0f );
  101. }
  102.  
  103. //-----------------------------------------------------------------------------
  104. // The player object class destructor.
  105. //-----------------------------------------------------------------------------
  106. PlayerObject::~PlayerObject()
  107. {
  108.     // Destroy the string buffer containing the player's name.
  109.     SAFE_DELETE( m_name );
  110. }
  111.  
  112. //-----------------------------------------------------------------------------
  113. // Updates the player object.
  114. //-----------------------------------------------------------------------------
  115. void PlayerObject::Update( float elapsed, bool addVelocity )
  116. {
  117.     // Allow the base animated object to update.
  118.     AnimatedObject::Update( elapsed, addVelocity );
  119.  
  120.     // Override the object's forward vector to take the view tilt into account.
  121.     // This will allow the forward vector to move up and down as well instead
  122.     // of just remaining horizontal. This is not important for movement since
  123.     // the player can not fly, but for things like shooting it is.
  124.     m_forward.x = (float)sin( GetRotation().y );
  125.     m_forward.y = (float)-tan( m_viewTilt );
  126.     m_forward.z = (float)cos( GetRotation().y );
  127.     D3DXVec3Normalize( &m_forward, &m_forward );
  128.  
  129.     // Set the player's view point. This is done every frame because as the
  130.     // mesh is animated, the reference point in the mesh may move. This
  131.     // will allow the view point to move with the mesh's animations.
  132.     m_viewPoint = GetMesh()->GetReferencePoint( "rp_view_point" )->GetTranslation();
  133.  
  134.     // Ensure that the view movement is relative to the rotation.
  135.     D3DXVec3TransformCoord( &m_viewPoint, &m_viewPoint, GetRotationMatrix() );
  136.  
  137.     // Only calculate the correct view matrix if it is being used.
  138.     if( m_isViewing == true )
  139.     {
  140.         // Create the x axis rotation matrix.
  141.         D3DXMATRIX rotationXMatrix;
  142.         D3DXMatrixRotationX( &rotationXMatrix, m_viewTilt );
  143.  
  144.         // Create the combined rotation matrix (i.e. y axis rotation from the
  145.         // scene object plus the x axis rotation from the player object).
  146.         D3DXMATRIX combinedRotation;
  147.         D3DXMatrixMultiply( &combinedRotation, &rotationXMatrix, GetRotationMatrix() );
  148.  
  149.         // Build a translation matrix that represents the final view point.
  150.         D3DXMATRIX viewPointTranslationMatrix;
  151.         D3DXVECTOR3 finalViewPointTranslation = GetTranslation() + m_viewPoint;
  152.         D3DXMatrixTranslation( &viewPointTranslationMatrix, finalViewPointTranslation.x, finalViewPointTranslation.y, finalViewPointTranslation.z );
  153.  
  154.         // Override the object's view matrix using the combined rotation and
  155.         // the position of the final view point translation.
  156.         D3DXMatrixMultiply( &m_viewMatrix, &combinedRotation, &viewPointTranslationMatrix );
  157.         D3DXMatrixInverse( &m_viewMatrix, NULL, &m_viewMatrix );
  158.     }
  159.  
  160.     // Ignore the rest if the player is dying (or dead)
  161.     if( m_dying == true )
  162.         return;
  163.  
  164.     // Drive and strafe the player accordingly.
  165.     if( m_drive != 0.0f )
  166.         Drive( m_drive * 8000.0f * elapsed );
  167.     if( m_strafe != 0.0f )
  168.         Strafe( m_strafe * 4000.0f * elapsed );
  169.  
  170.     // Update the step audio paths.
  171.     m_leftStepAudioPath->SetPosition( GetTranslation() + GetMesh()->GetReferencePoint( "rp_left_foot" )->GetTranslation() );
  172.     m_leftStepAudioPath->SetVelocity( GetVelocity() );
  173.     m_rightStepAudioPath->SetPosition( GetTranslation() + GetMesh()->GetReferencePoint( "rp_right_foot" )->GetTranslation() );
  174.     m_rightStepAudioPath->SetVelocity( GetVelocity() );
  175. }
  176.  
  177. //-----------------------------------------------------------------------------
  178. // Renders the player object.
  179. //-----------------------------------------------------------------------------
  180. void PlayerObject::Render( D3DXMATRIX *world )
  181. {
  182.     // Allow the base animated object to render.
  183.     if( m_dpnid != g_engine->GetNetwork()->GetLocalID() )
  184.         AnimatedObject::Render( world );
  185.     else if( m_dying == true )
  186.         return;
  187. }
  188.  
  189. //-----------------------------------------------------------------------------
  190. // Called when something collides with the object.
  191. //-----------------------------------------------------------------------------
  192. void PlayerObject::CollisionOccurred( SceneObject *object, unsigned long collisionStamp )
  193. {
  194.     // Ignore collisions if the player is dying (or dead)
  195.     if( m_dying == true )
  196.         return;
  197.  
  198.     // Allow the base scene object to register the collision.
  199.     SceneObject::CollisionOccurred( object, collisionStamp );
  200. }
  201.  
  202. //-----------------------------------------------------------------------------
  203. // Rotates the player's view.
  204. //-----------------------------------------------------------------------------
  205. void PlayerObject::MouseLook( float x, float y, bool reset )
  206. {
  207.     static float lastX = 0.0f;
  208.     static float lastY = 0.0f;
  209.  
  210.     // Check if the player's view needs to be reset.
  211.     if( reset == true )
  212.     {
  213.         lastX = lastY = 0.0f;
  214.         SetRotation( 0.0f, 0.0f, 0.0f );
  215.         m_viewTilt = 0.0f;
  216.         return;
  217.     }
  218.  
  219.     // Calculate the real x and y values by accounting for smoothing.
  220.     lastX = lastX * m_viewSmoothing + x * ( 1.0f - m_viewSmoothing );
  221.     lastY = lastY * m_viewSmoothing + y * ( 1.0f - m_viewSmoothing );
  222.  
  223.     // Adjust the values for sensitivity.
  224.     lastX *= m_viewSensitivity;
  225.     lastY *= m_viewSensitivity;
  226.  
  227.     // Rotate the scene object around the y axis only. This will prevent the
  228.     // player's mesh from rotating when the player looks up and down.
  229.     AddRotation( 0.0f, lastY, 0.0f );
  230.  
  231.     // Ensure the view will not rotate to far up or down.
  232.     if( ( m_viewTilt > 0.8f && lastX > 0.0f ) || ( m_viewTilt < -0.8f && lastX < 0.0f ) )
  233.         lastX = 0.0f;
  234.  
  235.     // Maintain a seperate view rotation around the x axis to allow the player
  236.     // to look up and down.
  237.     m_viewTilt += lastX;
  238. }
  239.  
  240. //-----------------------------------------------------------------------------
  241. // Hurts the player with the given damage.
  242. //-----------------------------------------------------------------------------
  243. void PlayerObject::Hurt( float damage, PlayerObject *attacker )
  244. {
  245.     // Ignore dying (or dead) players.
  246.     if( m_dying == true )
  247.         return;
  248.  
  249.     // Adjust the player's health.
  250.     m_health -= damage;
  251.  
  252.     // Send a player health update message.
  253.     PlayerHealthMsg phm;
  254.     phm.msgid = MSGID_PLAYER_HEALTH;
  255.     phm.dpnid = m_dpnid;
  256.     phm.health = m_health;
  257.     g_engine->GetNetwork()->Send( &phm, sizeof( PlayerHealthMsg ), DPNID_ALL_PLAYERS_GROUP, DPNSEND_NOLOOPBACK );
  258.  
  259.     // Check if the player is still alive (i.e. health above zero).
  260.     if( m_health > 0.0f )
  261.         return;
  262.  
  263.     // The player has been killed.
  264.     Kill();
  265.  
  266.     // Increment the player's death tally.
  267.     m_deaths += 1;
  268.  
  269.     // Send a player score update message for the fragged player.
  270.     PlayerScoreMsg psm1;
  271.     psm1.msgid = MSGID_PLAYER_SCORE;
  272.     psm1.dpnid = m_dpnid;
  273.     psm1.frags = m_frags;
  274.     psm1.deaths = m_deaths;
  275.     g_engine->GetNetwork()->Send( &psm1, sizeof( PlayerScoreMsg ), DPNID_ALL_PLAYERS_GROUP, DPNSEND_NOLOOPBACK );
  276.  
  277.     // Increment the attacking player's frag tally.
  278.     attacker->SetFrags( attacker->GetFrags() + 1 );
  279.  
  280.     // Send a player score update message for the bullet owner.
  281.     PlayerScoreMsg psm2;
  282.     psm2.msgid = MSGID_PLAYER_SCORE;
  283.     psm2.dpnid = attacker->GetID();
  284.     psm2.frags = attacker->GetFrags();
  285.     psm2.deaths = attacker->GetDeaths();
  286.     g_engine->GetNetwork()->Send( &psm2, sizeof( PlayerScoreMsg ), DPNID_ALL_PLAYERS_GROUP, DPNSEND_NOLOOPBACK );
  287. }
  288.  
  289. //-----------------------------------------------------------------------------
  290. // Kills the player.
  291. //-----------------------------------------------------------------------------
  292. void PlayerObject::Kill()
  293. {
  294.     // Indicate that the player is dying.
  295.     m_dying = true;
  296.  
  297.     // Clear the player's movment.
  298.     SetDrive( 0.0f );
  299.     SetStrafe( 0.0f );
  300.     SetFire( false );
  301.     Stop();
  302.  
  303.     // Play the death animation.
  304.     PlayAnimation( ANIM_DEATH, 0.0f, false );
  305. }
  306.  
  307. //-----------------------------------------------------------------------------
  308. // Returns the player object's DirectPlay ID number.
  309. //-----------------------------------------------------------------------------
  310. DPNID PlayerObject::GetID()
  311. {
  312.     return m_dpnid;
  313. }
  314.  
  315. //-----------------------------------------------------------------------------
  316. // Returns the player object's name.
  317. //-----------------------------------------------------------------------------
  318. char *PlayerObject::GetName()
  319. {
  320.     return m_name;
  321. }
  322.  
  323. //-----------------------------------------------------------------------------
  324. // Sets the player object's health.
  325. //-----------------------------------------------------------------------------
  326. void PlayerObject::SetHealth( float health )
  327. {
  328.     m_health = health;
  329. }
  330.  
  331. //-----------------------------------------------------------------------------
  332. // Returns the player object's health.
  333. //-----------------------------------------------------------------------------
  334. float PlayerObject::GetHealth()
  335. {
  336.     return m_health;
  337. }
  338.  
  339. //-----------------------------------------------------------------------------
  340. // Sets the player object's dying flag.
  341. //-----------------------------------------------------------------------------
  342. void PlayerObject::SetDying( bool dying )
  343. {
  344.     m_dying = dying;
  345. }
  346.  
  347. //-----------------------------------------------------------------------------
  348. // Returns the player object's dying flag.
  349. //-----------------------------------------------------------------------------
  350. bool PlayerObject::GetDying()
  351. {
  352.     return m_dying;
  353. }
  354.  
  355. //-----------------------------------------------------------------------------
  356. // Sets the player object's viewing flag.
  357. //-----------------------------------------------------------------------------
  358. void PlayerObject::SetIsViewing( bool isViewing )
  359. {
  360.     m_isViewing = isViewing;
  361. }
  362.  
  363. //-----------------------------------------------------------------------------
  364. // Sets the player object's frag count.
  365. //-----------------------------------------------------------------------------
  366. void PlayerObject::SetFrags( unsigned long frags )
  367. {
  368.     m_frags = frags;
  369. }
  370.  
  371. //-----------------------------------------------------------------------------
  372. // Returns the player object's frag count.
  373. //-----------------------------------------------------------------------------
  374. unsigned long PlayerObject::GetFrags()
  375. {
  376.     return m_frags;
  377. }
  378.  
  379. //-----------------------------------------------------------------------------
  380. // Sets the player object's death count.
  381. //-----------------------------------------------------------------------------
  382. void PlayerObject::SetDeaths( unsigned long deaths )
  383. {
  384.     m_deaths = deaths;
  385. }
  386.  
  387. //-----------------------------------------------------------------------------
  388. // Returns the player object's death count.
  389. //-----------------------------------------------------------------------------
  390. unsigned long PlayerObject::GetDeaths()
  391. {
  392.     return m_deaths;
  393. }
  394.  
  395. //-----------------------------------------------------------------------------
  396. // Sets the player object's drive.
  397. //-----------------------------------------------------------------------------
  398. void PlayerObject::SetDrive( float drive )
  399. {
  400.     // Ignore if the player is dying (or dead)
  401.     if( m_dying == true )
  402.         return;
  403.  
  404.     if( drive == 0.0f )
  405.     {
  406.         if( m_drive != 0.0f && m_strafe == 0.0f )
  407.             PlayAnimation( ANIM_IDLE, 0.2f );
  408.     }
  409.     else if( m_drive == 0.0f && m_strafe == 0.0f )
  410.     {
  411.         if( drive == 1.0f )
  412.             PlayAnimation( ANIM_FORWARDS, 0.2f );
  413.         else
  414.             PlayAnimation( ANIM_BACKWARDS, 0.2f );
  415.     }
  416.  
  417.     m_drive = drive;
  418. }
  419.  
  420. //-----------------------------------------------------------------------------
  421. // Returns the player object's current drive.
  422. //-----------------------------------------------------------------------------
  423. float PlayerObject::GetDrive()
  424. {
  425.     return m_drive;
  426. }
  427.  
  428. //-----------------------------------------------------------------------------
  429. // Sets the player object's strafe.
  430. //-----------------------------------------------------------------------------
  431. void PlayerObject::SetStrafe( float strafe )
  432. {
  433.     // Ignore if the player is dying (or dead)
  434.     if( m_dying == true )
  435.         return;
  436.  
  437.     if( strafe == 0.0f )
  438.     {
  439.         if( m_strafe != 0.0f && m_drive == 0.0f )
  440.             PlayAnimation( ANIM_IDLE, 0.2f );
  441.     }
  442.     else if( m_strafe == 0.0f && m_drive == 0.0f )
  443.     {
  444.         if( strafe == 1.0f )
  445.             PlayAnimation( ANIM_RIGHT, 0.2f );
  446.         else
  447.             PlayAnimation( ANIM_LEFT, 0.2f );
  448.     }
  449.  
  450.     m_strafe = strafe;
  451. }
  452.  
  453. //-----------------------------------------------------------------------------
  454. // Returns the player object's current strafe.
  455. //-----------------------------------------------------------------------------
  456. float PlayerObject::GetStrafe()
  457. {
  458.     return m_strafe;
  459. }
  460.  
  461. //-----------------------------------------------------------------------------
  462. // Sets the player object's fire flag.
  463. //-----------------------------------------------------------------------------
  464. void PlayerObject::SetFire( bool fire )
  465. {
  466.     // Ignore if the player is dying (or dead)
  467.     if( m_dying == true )
  468.         return;
  469.  
  470.     m_fire = fire;
  471. }
  472.  
  473. //-----------------------------------------------------------------------------
  474. // Returns the player object's fire flag.
  475. //-----------------------------------------------------------------------------
  476. bool PlayerObject::GetFire()
  477. {
  478.     return m_fire;
  479. }
  480.  
  481. //-----------------------------------------------------------------------------
  482. // Sets the player object's view tilt (i.e. the rotation around the x axis).
  483. //-----------------------------------------------------------------------------
  484. void PlayerObject::SetViewTilt( float tilt )
  485. {
  486.     m_viewTilt = tilt;
  487. }
  488.  
  489. //-----------------------------------------------------------------------------
  490. // Returns the player object's view tilt (i.e. the rotation around the x axis).
  491. //-----------------------------------------------------------------------------
  492. float PlayerObject::GetViewTilt()
  493. {
  494.     return m_viewTilt;
  495. }
  496.  
  497. //-----------------------------------------------------------------------------
  498. // Returns the player object's eye point.
  499. //-----------------------------------------------------------------------------
  500. D3DXVECTOR3 PlayerObject::GetEyePoint()
  501. {
  502.     return GetTranslation() + m_viewPoint;
  503. }
  504.  
  505. //-----------------------------------------------------------------------------
  506. // Animation call back handler.
  507. //-----------------------------------------------------------------------------
  508. HRESULT CALLBACK PlayerObject::HandleCallback( THIS_ UINT Track, LPVOID pCallbackData )
  509. {
  510.     // Get a pointer to the callback data.
  511.     AnimationCallbackData *data = (AnimationCallbackData*)pCallbackData;
  512.  
  513.     // If the player is not touching the ground, then it can't make foot steps.
  514.     if( IsTouchingGround() == false )
  515.         return S_OK;
  516.  
  517.     // Check which foot caused the callback.
  518.     if( data->foot == 1 )
  519.     {
  520.         // Reset the step result.
  521.         m_stepResult.material = NULL;
  522.  
  523.         // Preform a ray intersection for the left foot. If it is succesful,
  524.         // then play the material's step sound.
  525.         if( g_engine->GetSceneManager()->RayIntersectScene( &m_stepResult, GetTranslation() + GetMesh()->GetReferencePoint( "rp_left_foot" )->GetTranslation(), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) ) == true )
  526.         {
  527.             if( ( (GameMaterial*)m_stepResult.material )->GetStepSound() != NULL )
  528.                 m_leftStepAudioPath->Play( ( (GameMaterial*)m_stepResult.material )->GetStepSound()->GetSegment() );
  529.         }
  530.     }
  531.     else
  532.     {
  533.         // Reset the step result.
  534.         m_stepResult.material = NULL;
  535.  
  536.         // Preform a ray intersection for the right foot. If it is succesful,
  537.         // then play the material's step sound.
  538.         if( g_engine->GetSceneManager()->RayIntersectScene( &m_stepResult, GetTranslation() + GetMesh()->GetReferencePoint( "rp_right_foot" )->GetTranslation(), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) ) == true )
  539.         {
  540.             if( ( (GameMaterial*)m_stepResult.material )->GetStepSound() != NULL )
  541.                 m_rightStepAudioPath->Play( ( (GameMaterial*)m_stepResult.material )->GetStepSound()->GetSegment() );
  542.         }
  543.     }
  544.  
  545.     return S_OK;
  546. }